/*
 * AliceWebInstallerView.java
 */
package alicewebinstaller;

import org.jdesktop.application.Action;
import org.jdesktop.application.SingleFrameApplication;
import org.jdesktop.application.FrameView;
import org.jdesktop.application.Task;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URL;
import java.net.URLConnection;
import java.util.Enumeration;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import javax.swing.JFileChooser;
import javax.swing.JOptionPane;

/**
 * The application's main frame.
 */
public class AliceWebInstallerView extends FrameView {

    public AliceWebInstallerView(SingleFrameApplication app) {
        super(app);
        initComponents();
        setInstallDir(app);
        download().execute();
    }

    /** This method is called from within the constructor to
     * initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        mainPanel = new javax.swing.JPanel();
        progressBar = new javax.swing.JProgressBar();
        closeButton = new javax.swing.JButton();
        statusPanel = new javax.swing.JPanel();
        statusMessageLabel = new javax.swing.JLabel();

        mainPanel.setMaximumSize(new java.awt.Dimension(400, 120));
        mainPanel.setName("mainPanel"); // NOI18N
        mainPanel.setPreferredSize(new java.awt.Dimension(400, 120));

        progressBar.setName("progressBar"); // NOI18N

        javax.swing.ActionMap actionMap = org.jdesktop.application.Application.getInstance(alicewebinstaller.AliceWebInstallerApp.class).getContext().getActionMap(AliceWebInstallerView.class, this);
        closeButton.setAction(actionMap.get("quit")); // NOI18N
        org.jdesktop.application.ResourceMap resourceMap = org.jdesktop.application.Application.getInstance(alicewebinstaller.AliceWebInstallerApp.class).getContext().getResourceMap(AliceWebInstallerView.class);
        closeButton.setText(resourceMap.getString("closeButton.text")); // NOI18N
        closeButton.setName("closeButton"); // NOI18N

        statusPanel.setName("statusPanel"); // NOI18N

        statusMessageLabel.setText(resourceMap.getString("statusMessageLabel.text")); // NOI18N
        statusMessageLabel.setName("statusMessageLabel"); // NOI18N

        javax.swing.GroupLayout statusPanelLayout = new javax.swing.GroupLayout(statusPanel);
        statusPanel.setLayout(statusPanelLayout);
        statusPanelLayout.setHorizontalGroup(
            statusPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(statusPanelLayout.createSequentialGroup()
                .addContainerGap()
                .addComponent(statusMessageLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 378, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
        );
        statusPanelLayout.setVerticalGroup(
            statusPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(statusPanelLayout.createSequentialGroup()
                .addContainerGap()
                .addComponent(statusMessageLabel)
                .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
        );

        javax.swing.GroupLayout mainPanelLayout = new javax.swing.GroupLayout(mainPanel);
        mainPanel.setLayout(mainPanelLayout);
        mainPanelLayout.setHorizontalGroup(
            mainPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, mainPanelLayout.createSequentialGroup()
                .addContainerGap(335, Short.MAX_VALUE)
                .addComponent(closeButton)
                .addContainerGap())
            .addComponent(statusPanel, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
            .addGroup(mainPanelLayout.createSequentialGroup()
                .addContainerGap()
                .addComponent(progressBar, javax.swing.GroupLayout.DEFAULT_SIZE, 378, Short.MAX_VALUE)
                .addContainerGap())
        );
        mainPanelLayout.setVerticalGroup(
            mainPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(mainPanelLayout.createSequentialGroup()
                .addComponent(statusPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(progressBar, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 11, Short.MAX_VALUE)
                .addComponent(closeButton)
                .addContainerGap())
        );

        setComponent(mainPanel);
    }// </editor-fold>//GEN-END:initComponents

    @Action
    public Task download() {
        return new DownloadTask(getApplication());
    }

    /**
     * Downloads in a background thread to keep the GUI usable.
     */
    private class DownloadTask extends org.jdesktop.application.Task<Object, Void> {

        DownloadTask(org.jdesktop.application.Application app) {
            super(app);
        }

        @Override
        protected Object doInBackground() {
            URLConnection connection;
            BufferedInputStream inStream = null;
            BufferedOutputStream outStream = null;
            int downloadSize, bytesRead, currentByte, returnCode;
            byte[] data;

            returnCode = 0;
            statusMessageLabel.setText("Downloading " + ALICE_URL);
            progressBar.setValue(0);

            try {
                // Connect
                connection = new URL(ALICE_URL).openConnection();
                downloadSize = connection.getContentLength();
                outStream = new BufferedOutputStream(new FileOutputStream(FILE_NAME), BUFFER);
                inStream = new BufferedInputStream(connection.getInputStream());
                bytesRead = 0;
                progressBar.setMaximum(downloadSize);
                data = new byte[BUFFER];

                // Download
                while ((currentByte = inStream.read(data, 0, BUFFER)) != -1) {
                    outStream.write(data, 0, currentByte);
                    bytesRead += currentByte;
                    statusMessageLabel.setText("Read " + bytesRead + " bytes of " + downloadSize);
                    progressBar.setValue(bytesRead);
                }
                outStream.flush();
                outStream.close();
                inStream.close();

                // Verify
                if (bytesRead != downloadSize) {
                    returnCode = 2;
                } else {
                    statusMessageLabel.setText("Download complete");
                }
            } catch (IOException ex) {
                returnCode = 1;
            } finally {
                if (inStream != null) {
                    try {
                        inStream.close();
                    } catch (IOException ex) {
                        returnCode = 1;
                    }
                }
                if (outStream != null) {
                    try {
                        outStream.close();
                    } catch (IOException ex) {
                        returnCode = 1;
                    }
                }
            }
            return returnCode;
        }

        @Override
        protected void succeeded(Object result) {
            switch ((Integer) result) {
                case 1:
                    JOptionPane.showMessageDialog(null,
                            "There was a problem saving the file.",
                            "Download failed",
                            JOptionPane.ERROR_MESSAGE);
                    break;
                case 2:
                    JOptionPane.showMessageDialog(null,
                            "Download is corrupted.",
                            "Download failed",
                            JOptionPane.ERROR_MESSAGE);
                    break;
                default:
                    unzip().execute();
            }
        }
    }

    @Action
    public Task unzip() {
        return new UnzipTask(getApplication());
    }

    /**
     * Unzips in a background thread to keep the GUI usable.
     */
    private class UnzipTask extends org.jdesktop.application.Task<Object, Void> {

        UnzipTask(org.jdesktop.application.Application app) {
            super(app);
        }

        @Override
        protected Object doInBackground() {
            int currentByte, returnCode;
            byte[] data;
            String currentEntry;
            File aliceZip;
            File destFile;
            ZipFile zipFile;
            ZipEntry zipEntry;
            BufferedInputStream inStream;
            BufferedOutputStream outStream;
            Enumeration<? extends ZipEntry> zipFileEntries;

            returnCode = 0;
            statusMessageLabel.setText("Unzipping " + FILE_NAME);
            progressBar.setIndeterminate(true);
            aliceZip = new File(FILE_NAME);

            try {
                if (!installDir.exists()) {
                    if (installDir.mkdir() == false) {
                        throw new IOException();
                    }
                }
                zipFile = new ZipFile(aliceZip, ZipFile.OPEN_READ);
                zipFileEntries = zipFile.entries();
                data = new byte[BUFFER];

                // Unzip files
                while (zipFileEntries.hasMoreElements()) {
                    zipEntry = zipFileEntries.nextElement();
                    currentEntry = zipEntry.getName();
                    statusMessageLabel.setText("Unzipping " + currentEntry);
                    destFile = new File(installDir, currentEntry);
                    if (!destFile.getParentFile().exists()) {
                        if (destFile.getParentFile().mkdirs() == false) {
                            throw new IOException();
                        }
                    }

                    // Write file
                    if (!zipEntry.isDirectory()) {
                        outStream = new BufferedOutputStream(new FileOutputStream(destFile), BUFFER);
                        inStream = new BufferedInputStream(zipFile.getInputStream(zipEntry));

                        while ((currentByte = inStream.read(data, 0, BUFFER)) != -1) {
                            outStream.write(data, 0, currentByte);
                        }
                        outStream.flush();
                        outStream.close();
                        inStream.close();
                    }
                }
                progressBar.setIndeterminate(false);
                statusMessageLabel.setText("Unzip complete");
                zipFile.close();
            } catch (IOException ex) {
                returnCode = 1;
            }
            return returnCode;
        }

        @Override
        protected void succeeded(Object result) {
            if ((Integer) result == 1) {
                JOptionPane.showMessageDialog(null,
                        "There was a problem unzipping the file.",
                        "Extraction failed",
                        JOptionPane.ERROR_MESSAGE);
            } else {
                getApplication().exit();
            }
        }
    }

    /**
     * Asks user if we may install to the default location.
     * If not, lets the user choose where to install.
     */
    private void setInstallDir(SingleFrameApplication app) {
        JOptionPane pane = new JOptionPane(this, JOptionPane.QUESTION_MESSAGE, JOptionPane.YES_NO_CANCEL_OPTION);
        pane.setMessage("Install in default location? (Home dir)");
        pane.createDialog("Alice Web Installer").setVisible(true);
        switch ((Integer) pane.getValue()) {
            case JOptionPane.NO_OPTION:
                JFileChooser fileChooser = new JFileChooser(System.getProperty("user.home"));
                fileChooser.setDialogTitle("Choose install directory");
                fileChooser.setApproveButtonText("Install");
                fileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
                if (fileChooser.showSaveDialog(mainPanel) == JFileChooser.APPROVE_OPTION) {
                    installDir = fileChooser.getSelectedFile();
                } else {
                    app.exit();
                }
                break;
            case JOptionPane.CANCEL_OPTION:
                app.exit();
                break;
            default:
        }
    }
    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JButton closeButton;
    private javax.swing.JPanel mainPanel;
    private javax.swing.JProgressBar progressBar;
    private javax.swing.JLabel statusMessageLabel;
    private javax.swing.JPanel statusPanel;
    // End of variables declaration//GEN-END:variables
    private final String ALICE_URL = "http://www.alice.org/kelleher/storytelling/StorytellingAlice.zip";
    private final String FILE_NAME = "SA.zip"; // TODO: Delete this file when finished
    private final int BUFFER = 4096; // Using 4 KB buffer
    private File installDir = new File(System.getProperty("user.home"));  // TODO: Pick a good install dir based on OS
}
